// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/layers/layer_impl.h"

#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/solid_color_scrollbar_layer_impl.h"
#include "cc/output/filter_operation.h"
#include "cc/output/filter_operations.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_compositor_frame_sink.h"
#include "cc/test/fake_impl_task_runner_provider.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/test_task_graph_runner.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/mutable_properties.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/tree_synchronizer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/effects/SkBlurImageFilter.h"

namespace cc {
namespace {

#define EXECUTE_AND_VERIFY_SUBTREE_CHANGED(code_to_test)                      \
    root->layer_tree_impl()->ResetAllChangeTracking();                        \
    code_to_test;                                                             \
    EXPECT_TRUE(                                                              \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(root));   \
    EXPECT_FALSE(                                                             \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(child));  \
    EXPECT_FALSE(root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting( \
        grand_child));                                                        \
    EXPECT_TRUE(root->LayerPropertyChanged());                                \
    EXPECT_TRUE(child->LayerPropertyChanged());                               \
    EXPECT_TRUE(grand_child->LayerPropertyChanged());

#define EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(code_to_test)               \
    root->layer_tree_impl()->ResetAllChangeTracking();                        \
    code_to_test;                                                             \
    EXPECT_FALSE(                                                             \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(root));   \
    EXPECT_FALSE(                                                             \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(child));  \
    EXPECT_FALSE(root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting( \
        grand_child));                                                        \
    EXPECT_FALSE(root->LayerPropertyChanged());                               \
    EXPECT_FALSE(child->LayerPropertyChanged());                              \
    EXPECT_FALSE(grand_child->LayerPropertyChanged());

#define EXECUTE_AND_VERIFY_NEEDS_PUSH_PROPERTIES_AND_SUBTREE_DID_NOT_CHANGE(  \
    code_to_test)                                                             \
    root->layer_tree_impl()->ResetAllChangeTracking();                        \
    code_to_test;                                                             \
    EXPECT_TRUE(                                                              \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(root));   \
    EXPECT_FALSE(                                                             \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(child));  \
    EXPECT_FALSE(root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting( \
        grand_child));                                                        \
    EXPECT_FALSE(root->LayerPropertyChanged());                               \
    EXPECT_FALSE(child->LayerPropertyChanged());                              \
    EXPECT_FALSE(grand_child->LayerPropertyChanged());

#define EXECUTE_AND_VERIFY_NO_NEED_TO_PUSH_PROPERTIES_AND_SUBTREE_CHANGED(    \
    code_to_test)                                                             \
    root->layer_tree_impl()->ResetAllChangeTracking();                        \
    code_to_test;                                                             \
    EXPECT_FALSE(                                                             \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(root));   \
    EXPECT_FALSE(                                                             \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(child));  \
    EXPECT_FALSE(root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting( \
        grand_child));                                                        \
    EXPECT_TRUE(root->LayerPropertyChanged());                                \
    EXPECT_TRUE(child->LayerPropertyChanged());                               \
    EXPECT_TRUE(grand_child->LayerPropertyChanged());

#define EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(code_to_test)                   \
    root->layer_tree_impl()->ResetAllChangeTracking();                        \
    root->layer_tree_impl()->property_trees()->full_tree_damaged = false;     \
    code_to_test;                                                             \
    EXPECT_TRUE(                                                              \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(root));   \
    EXPECT_FALSE(                                                             \
        root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting(child));  \
    EXPECT_FALSE(root->layer_tree_impl()->LayerNeedsPushPropertiesForTesting( \
        grand_child));                                                        \
    EXPECT_TRUE(root->LayerPropertyChanged());                                \
    EXPECT_FALSE(child->LayerPropertyChanged());                              \
    EXPECT_FALSE(grand_child->LayerPropertyChanged());

#define VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(code_to_test)                  \
    root->layer_tree_impl()->ResetAllChangeTracking();                     \
    host_impl.ForcePrepareToDraw();                                        \
    EXPECT_FALSE(host_impl.active_tree()->needs_update_draw_properties()); \
    code_to_test;                                                          \
    EXPECT_TRUE(host_impl.active_tree()->needs_update_draw_properties());

#define VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(code_to_test)               \
    root->layer_tree_impl()->ResetAllChangeTracking();                     \
    host_impl.ForcePrepareToDraw();                                        \
    EXPECT_FALSE(host_impl.active_tree()->needs_update_draw_properties()); \
    code_to_test;                                                          \
    EXPECT_FALSE(host_impl.active_tree()->needs_update_draw_properties());

    static gfx::Vector2dF ScrollDelta(LayerImpl* layer_impl)
    {
        gfx::ScrollOffset delta = layer_impl->layer_tree_impl()
                                      ->property_trees()
                                      ->scroll_tree.GetScrollOffsetDeltaForTesting(layer_impl->id());
        return gfx::Vector2dF(delta.x(), delta.y());
    }

    TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly)
    {
        //
        // This test checks that layerPropertyChanged() has the correct behavior.
        //

        // The constructor on this will fake that we are on the correct thread.
        // Create a simple LayerImpl tree:
        FakeImplTaskRunnerProvider task_runner_provider;
        TestTaskGraphRunner task_graph_runner;
        std::unique_ptr<CompositorFrameSink> compositor_frame_sink = FakeCompositorFrameSink::Create3d();
        FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
        host_impl.SetVisible(true);
        EXPECT_TRUE(host_impl.InitializeRenderer(compositor_frame_sink.get()));
        std::unique_ptr<LayerImpl> root_clip_ptr = LayerImpl::Create(host_impl.active_tree(), 1);
        LayerImpl* root_clip = root_clip_ptr.get();
        std::unique_ptr<LayerImpl> root_ptr = LayerImpl::Create(host_impl.active_tree(), 2);
        LayerImpl* root = root_ptr.get();
        root_clip_ptr->test_properties()->AddChild(std::move(root_ptr));
        host_impl.active_tree()->SetRootLayerForTesting(std::move(root_clip_ptr));

        root->test_properties()->force_render_surface = true;
        root->layer_tree_impl()->ResetAllChangeTracking();

        root->test_properties()->AddChild(
            LayerImpl::Create(host_impl.active_tree(), 7));
        LayerImpl* child = root->test_properties()->children[0];
        child->test_properties()->AddChild(
            LayerImpl::Create(host_impl.active_tree(), 8));
        LayerImpl* grand_child = child->test_properties()->children[0];
        root->SetScrollClipLayer(root_clip->id());
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

        // Adding children is an internal operation and should not mark layers as
        // changed.
        EXPECT_FALSE(root->LayerPropertyChanged());
        EXPECT_FALSE(child->LayerPropertyChanged());
        EXPECT_FALSE(grand_child->LayerPropertyChanged());

        gfx::PointF arbitrary_point_f = gfx::PointF(0.125f, 0.25f);
        float arbitrary_number = 0.352f;
        gfx::Size arbitrary_size = gfx::Size(111, 222);
        gfx::Point arbitrary_point = gfx::Point(333, 444);
        gfx::Vector2d arbitrary_vector2d = gfx::Vector2d(111, 222);
        gfx::Rect arbitrary_rect = gfx::Rect(arbitrary_point, arbitrary_size);
        SkColor arbitrary_color = SkColorSetRGB(10, 20, 30);
        gfx::Transform arbitrary_transform;
        arbitrary_transform.Scale3d(0.1f, 0.2f, 0.3f);
        FilterOperations arbitrary_filters;
        arbitrary_filters.Append(FilterOperation::CreateOpacityFilter(0.5f));

        // These properties are internal, and should not be considered "change" when
        // they are used.
        EXECUTE_AND_VERIFY_NEEDS_PUSH_PROPERTIES_AND_SUBTREE_DID_NOT_CHANGE(
            root->SetUpdateRect(arbitrary_rect));
        EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetBounds(arbitrary_size));
        host_impl.active_tree()->property_trees()->needs_rebuild = true;
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

        // Changing these properties affects the entire subtree of layers.
        EXECUTE_AND_VERIFY_NO_NEED_TO_PUSH_PROPERTIES_AND_SUBTREE_CHANGED(
            host_impl.active_tree()->property_trees()->effect_tree.OnFilterAnimated(
                arbitrary_filters, root->effect_tree_index(),
                host_impl.active_tree()));
        EXECUTE_AND_VERIFY_NO_NEED_TO_PUSH_PROPERTIES_AND_SUBTREE_CHANGED(
            host_impl.active_tree()->property_trees()->effect_tree.OnFilterAnimated(
                FilterOperations(), root->effect_tree_index(),
                host_impl.active_tree()));
        EXECUTE_AND_VERIFY_NO_NEED_TO_PUSH_PROPERTIES_AND_SUBTREE_CHANGED(
            host_impl.active_tree()->property_trees()->effect_tree.OnOpacityAnimated(
                arbitrary_number, root->effect_tree_index(),
                host_impl.active_tree()));
        EXECUTE_AND_VERIFY_NO_NEED_TO_PUSH_PROPERTIES_AND_SUBTREE_CHANGED(
            host_impl.active_tree()
                ->property_trees()
                ->transform_tree.OnTransformAnimated(arbitrary_transform,
                    root->transform_tree_index(),
                    host_impl.active_tree()));
        EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->ScrollBy(arbitrary_vector2d);
                                           root->SetNeedsPushProperties());
        // SetBoundsDelta changes subtree only when masks_to_bounds is true and it
        // doesn't set needs_push_properties as it is always called on active tree.
        root->SetMasksToBounds(true);
        EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetBoundsDelta(arbitrary_vector2d);
                                           root->SetNeedsPushProperties());

        // Changing these properties only affects the layer itself.
        EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetDrawsContent(true));
        EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(
            root->SetBackgroundColor(arbitrary_color));

        // Special case: check that SetBounds changes behavior depending on
        // masksToBounds.
        gfx::Size bounds_size(135, 246);
        root->SetMasksToBounds(false);
        EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetBounds(bounds_size));
        host_impl.active_tree()->property_trees()->needs_rebuild = true;
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

        root->SetMasksToBounds(true);
        host_impl.active_tree()->property_trees()->needs_rebuild = true;
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

        // Changing these properties does not cause the layer to be marked as changed
        // but does cause the layer to need to push properties.
        EXECUTE_AND_VERIFY_NEEDS_PUSH_PROPERTIES_AND_SUBTREE_DID_NOT_CHANGE(
            root->SetElementId(ElementId(2, 0)));
        EXECUTE_AND_VERIFY_NEEDS_PUSH_PROPERTIES_AND_SUBTREE_DID_NOT_CHANGE(
            root->SetMutableProperties(MutableProperty::kOpacity);
            root->SetNeedsPushProperties());

        // After setting all these properties already, setting to the exact same
        // values again should not cause any change.
        EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetMasksToBounds(true));
        EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(
            root->SetPosition(arbitrary_point_f));
        EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetContentsOpaque(true));
        EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetDrawsContent(true));
        EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetBounds(bounds_size));
    }

    TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties)
    {
        FakeImplTaskRunnerProvider task_runner_provider;
        TestTaskGraphRunner task_graph_runner;
        std::unique_ptr<CompositorFrameSink> compositor_frame_sink = FakeCompositorFrameSink::Create3d();
        FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
        host_impl.SetVisible(true);
        EXPECT_TRUE(host_impl.InitializeRenderer(compositor_frame_sink.get()));
        host_impl.active_tree()->SetRootLayerForTesting(
            LayerImpl::Create(host_impl.active_tree(), 1));
        LayerImpl* root = host_impl.active_tree()->root_layer_for_testing();
        root->SetHasRenderSurface(true);
        std::unique_ptr<LayerImpl> layer_ptr = LayerImpl::Create(host_impl.active_tree(), 2);
        LayerImpl* layer = layer_ptr.get();
        root->test_properties()->AddChild(std::move(layer_ptr));
        layer->SetScrollClipLayer(root->id());
        std::unique_ptr<LayerImpl> layer2_ptr = LayerImpl::Create(host_impl.active_tree(), 3);
        LayerImpl* layer2 = layer2_ptr.get();
        root->test_properties()->AddChild(std::move(layer2_ptr));
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
        DCHECK(host_impl.CanDraw());

        gfx::PointF arbitrary_point_f = gfx::PointF(0.125f, 0.25f);
        float arbitrary_number = 0.352f;
        gfx::Size arbitrary_size = gfx::Size(111, 222);
        gfx::Vector2d arbitrary_vector2d = gfx::Vector2d(111, 222);
        gfx::Size large_size = gfx::Size(1000, 1000);
        SkColor arbitrary_color = SkColorSetRGB(10, 20, 30);
        gfx::Transform arbitrary_transform;
        arbitrary_transform.Scale3d(0.1f, 0.2f, 0.3f);
        FilterOperations arbitrary_filters;
        arbitrary_filters.Append(FilterOperation::CreateOpacityFilter(0.5f));

        // Set layer to draw content so that their draw property by property trees is
        // verified.
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetDrawsContent(true));
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer2->SetDrawsContent(true));
        // Render surface functions should not trigger update draw properties, because
        // creating render surface is part of update draw properties.
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetHasRenderSurface(true));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetHasRenderSurface(false));
        // Create a render surface, because we must have a render surface if we have
        // filters.
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetHasRenderSurface(true));

        // Related filter functions.
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
            host_impl.active_tree()->property_trees()->effect_tree.OnFilterAnimated(
                arbitrary_filters, root->effect_tree_index(),
                host_impl.active_tree()));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
            host_impl.active_tree()->property_trees()->effect_tree.OnFilterAnimated(
                arbitrary_filters, root->effect_tree_index(),
                host_impl.active_tree()));
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
            host_impl.active_tree()->property_trees()->effect_tree.OnFilterAnimated(
                FilterOperations(), root->effect_tree_index(),
                host_impl.active_tree()));
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
            host_impl.active_tree()->property_trees()->effect_tree.OnFilterAnimated(
                arbitrary_filters, root->effect_tree_index(),
                host_impl.active_tree()));

        // Related scrolling functions.
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(large_size));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(large_size));
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->ScrollBy(arbitrary_vector2d));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->ScrollBy(gfx::Vector2d()));
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
            layer->layer_tree_impl()->DidUpdateScrollOffset(layer->id()));
        layer->layer_tree_impl()
            ->property_trees()
            ->scroll_tree.SetScrollOffsetDeltaForTesting(layer->id(),
                gfx::Vector2dF());
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetCurrentScrollOffset(
            gfx::ScrollOffset(arbitrary_vector2d.x(), arbitrary_vector2d.y())));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetCurrentScrollOffset(
            gfx::ScrollOffset(arbitrary_vector2d.x(), arbitrary_vector2d.y())));

        // Unrelated functions, always set to new values, always set needs update.
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetMasksToBounds(true);
                                            layer->NoteLayerPropertyChanged());
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetContentsOpaque(true);
                                            layer->NoteLayerPropertyChanged());
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer2->SetPosition(arbitrary_point_f);
                                            layer->NoteLayerPropertyChanged());
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
            layer->SetBackgroundColor(arbitrary_color));
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
            host_impl.active_tree()->property_trees()->effect_tree.OnOpacityAnimated(
                arbitrary_number, layer->effect_tree_index(),
                host_impl.active_tree()));
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
            host_impl.active_tree()
                ->property_trees()
                ->transform_tree.OnTransformAnimated(arbitrary_transform,
                    layer->transform_tree_index(),
                    host_impl.active_tree()));
        VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(arbitrary_size);
                                            layer->NoteLayerPropertyChanged());

        // Unrelated functions, set to the same values, no needs update.
        layer->test_properties()->filters = arbitrary_filters;
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
            host_impl.active_tree()->property_trees()->effect_tree.OnFilterAnimated(
                arbitrary_filters, layer->effect_tree_index(),
                host_impl.active_tree()));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetMasksToBounds(true));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetContentsOpaque(true));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
            layer2->SetPosition(arbitrary_point_f));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetDrawsContent(true));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
            layer->SetBackgroundColor(arbitrary_color));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(arbitrary_size));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetElementId(ElementId(2, 0)));
        VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
            layer->SetMutableProperties(MutableProperty::kTransform));
    }

    TEST(LayerImplTest, SafeOpaqueBackgroundColor)
    {
        FakeImplTaskRunnerProvider task_runner_provider;
        TestTaskGraphRunner task_graph_runner;
        std::unique_ptr<CompositorFrameSink> compositor_frame_sink = FakeCompositorFrameSink::Create3d();
        FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
        host_impl.SetVisible(true);
        EXPECT_TRUE(host_impl.InitializeRenderer(compositor_frame_sink.get()));
        host_impl.active_tree()->SetRootLayerForTesting(
            LayerImpl::Create(host_impl.active_tree(), 1));
        LayerImpl* layer = host_impl.active_tree()->root_layer_for_testing();

        for (int contents_opaque = 0; contents_opaque < 2; ++contents_opaque) {
            for (int layer_opaque = 0; layer_opaque < 2; ++layer_opaque) {
                for (int host_opaque = 0; host_opaque < 2; ++host_opaque) {
                    layer->SetContentsOpaque(!!contents_opaque);
                    layer->SetBackgroundColor(layer_opaque ? SK_ColorRED
                                                           : SK_ColorTRANSPARENT);
                    host_impl.active_tree()->set_background_color(
                        host_opaque ? SK_ColorRED : SK_ColorTRANSPARENT);
                    host_impl.active_tree()->property_trees()->needs_rebuild = true;
                    host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

                    SkColor safe_color = layer->SafeOpaqueBackgroundColor();
                    if (contents_opaque) {
                        EXPECT_EQ(SkColorGetA(safe_color), 255u)
                            << "Flags: " << contents_opaque << ", " << layer_opaque << ", "
                            << host_opaque << "\n";
                    } else {
                        EXPECT_NE(SkColorGetA(safe_color), 255u)
                            << "Flags: " << contents_opaque << ", " << layer_opaque << ", "
                            << host_opaque << "\n";
                    }
                }
            }
        }
    }

    class LayerImplScrollTest : public testing::Test {
    public:
        LayerImplScrollTest()
            : host_impl_(settings(),
                &task_runner_provider_,
                &task_graph_runner_)
            , root_id_(7)
        {
            host_impl_.active_tree()->SetRootLayerForTesting(
                LayerImpl::Create(host_impl_.active_tree(), root_id_));
            host_impl_.active_tree()
                ->root_layer_for_testing()
                ->test_properties()
                ->AddChild(LayerImpl::Create(host_impl_.active_tree(), root_id_ + 1));
            layer()->SetScrollClipLayer(root_id_);
            // Set the max scroll offset by noting that the root layer has bounds (1,1),
            // thus whatever bounds are set for the layer will be the max scroll
            // offset plus 1 in each direction.
            host_impl_.active_tree()->root_layer_for_testing()->SetBounds(
                gfx::Size(1, 1));
            gfx::Vector2d max_scroll_offset(51, 81);
            layer()->SetBounds(gfx::Size(max_scroll_offset.x(), max_scroll_offset.y()));
            host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
        }

        LayerImpl* layer()
        {
            return host_impl_.active_tree()
                ->root_layer_for_testing()
                ->test_properties()
                ->children[0];
        }

        ScrollTree* scroll_tree(LayerImpl* layer_impl)
        {
            return &layer_impl->layer_tree_impl()->property_trees()->scroll_tree;
        }

        LayerTreeHostImpl& host_impl() { return host_impl_; }

        LayerTreeImpl* tree() { return host_impl_.active_tree(); }

        LayerTreeSettings settings()
        {
            LayerTreeSettings settings;
            settings.verify_clip_tree_calculations = true;
            return settings;
        }

    private:
        FakeImplTaskRunnerProvider task_runner_provider_;
        TestTaskGraphRunner task_graph_runner_;
        FakeLayerTreeHostImpl host_impl_;
        int root_id_;
    };

    TEST_F(LayerImplScrollTest, ScrollByWithZeroOffset)
    {
        // Test that LayerImpl::ScrollBy only affects ScrollDelta and total scroll
        // offset is bounded by the range [0, max scroll offset].

        EXPECT_VECTOR_EQ(gfx::Vector2dF(), layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(
            gfx::Vector2dF(),
            scroll_tree(layer())->GetScrollOffsetBaseForTesting(layer()->id()));
        EXPECT_VECTOR_EQ(gfx::Vector2dF(), ScrollDelta(layer()));

        layer()->ScrollBy(gfx::Vector2dF(-100, 100));
        EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 80), layer()->CurrentScrollOffset());

        EXPECT_VECTOR_EQ(ScrollDelta(layer()), layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(
            gfx::Vector2dF(),
            scroll_tree(layer())->GetScrollOffsetBaseForTesting(layer()->id()));

        layer()->ScrollBy(gfx::Vector2dF(100, -100));
        EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 0), layer()->CurrentScrollOffset());

        EXPECT_VECTOR_EQ(ScrollDelta(layer()), layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(
            gfx::Vector2dF(),
            scroll_tree(layer())->GetScrollOffsetBaseForTesting(layer()->id()));
    }

    TEST_F(LayerImplScrollTest, ScrollByWithNonZeroOffset)
    {
        gfx::ScrollOffset scroll_offset(10, 5);
        scroll_tree(layer())->UpdateScrollOffsetBaseForTesting(layer()->id(),
            scroll_offset);

        EXPECT_VECTOR_EQ(scroll_offset, layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(
            scroll_offset,
            scroll_tree(layer())->GetScrollOffsetBaseForTesting(layer()->id()));
        EXPECT_VECTOR_EQ(gfx::Vector2dF(), ScrollDelta(layer()));

        layer()->ScrollBy(gfx::Vector2dF(-100, 100));
        EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 80), layer()->CurrentScrollOffset());

        EXPECT_VECTOR_EQ(
            gfx::ScrollOffsetWithDelta(scroll_offset, ScrollDelta(layer())),
            layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(
            scroll_offset,
            scroll_tree(layer())->GetScrollOffsetBaseForTesting(layer()->id()));

        layer()->ScrollBy(gfx::Vector2dF(100, -100));
        EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 0), layer()->CurrentScrollOffset());

        EXPECT_VECTOR_EQ(
            gfx::ScrollOffsetWithDelta(scroll_offset, ScrollDelta(layer())),
            layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(
            scroll_offset,
            scroll_tree(layer())->GetScrollOffsetBaseForTesting(layer()->id()));
    }

    TEST_F(LayerImplScrollTest, ApplySentScrollsNoListener)
    {
        gfx::ScrollOffset scroll_offset(10, 5);
        gfx::Vector2dF scroll_delta(20.5f, 8.5f);
        gfx::Vector2d sent_scroll_delta(12, -3);

        scroll_tree(layer())->UpdateScrollOffsetBaseForTesting(layer()->id(),
            scroll_offset);
        layer()->ScrollBy(sent_scroll_delta);
        scroll_tree(layer())->CollectScrollDeltasForTesting();
        layer()->SetCurrentScrollOffset(scroll_offset + gfx::ScrollOffset(scroll_delta));

        EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(scroll_offset, scroll_delta),
            layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(scroll_delta, ScrollDelta(layer()));
        EXPECT_VECTOR_EQ(
            scroll_offset,
            scroll_tree(layer())->GetScrollOffsetBaseForTesting(layer()->id()));

        scroll_tree(layer())->ApplySentScrollDeltasFromAbortedCommit();

        EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(scroll_offset, scroll_delta),
            layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(scroll_delta - sent_scroll_delta, ScrollDelta(layer()));
        EXPECT_VECTOR_EQ(
            gfx::ScrollOffsetWithDelta(scroll_offset, sent_scroll_delta),
            scroll_tree(layer())->GetScrollOffsetBaseForTesting(layer()->id()));
    }

    TEST_F(LayerImplScrollTest, ScrollUserUnscrollableLayer)
    {
        gfx::ScrollOffset scroll_offset(10, 5);
        gfx::Vector2dF scroll_delta(20.5f, 8.5f);

        layer()->set_user_scrollable_vertical(false);
        layer()->layer_tree_impl()->property_trees()->needs_rebuild = true;
        layer()->layer_tree_impl()->BuildLayerListAndPropertyTreesForTesting();
        scroll_tree(layer())->UpdateScrollOffsetBaseForTesting(layer()->id(),
            scroll_offset);
        gfx::Vector2dF unscrolled = layer()->ScrollBy(scroll_delta);

        EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 8.5f), unscrolled);
        EXPECT_VECTOR_EQ(gfx::Vector2dF(30.5f, 5), layer()->CurrentScrollOffset());
    }

    TEST_F(LayerImplScrollTest, PushPropertiesToMirrorsCurrentScrollOffset)
    {
        gfx::ScrollOffset scroll_offset(10, 5);
        gfx::Vector2dF scroll_delta(12, 18);

        host_impl().CreatePendingTree();

        scroll_tree(layer())->UpdateScrollOffsetBaseForTesting(layer()->id(),
            scroll_offset);
        gfx::Vector2dF unscrolled = layer()->ScrollBy(scroll_delta);

        EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), unscrolled);
        EXPECT_VECTOR_EQ(gfx::Vector2dF(22, 23), layer()->CurrentScrollOffset());

        scroll_tree(layer())->CollectScrollDeltasForTesting();

        std::unique_ptr<LayerImpl> pending_layer = LayerImpl::Create(host_impl().sync_tree(), layer()->id());
        scroll_tree(pending_layer.get())
            ->UpdateScrollOffsetBaseForTesting(pending_layer->id(),
                layer()->CurrentScrollOffset());

        pending_layer->PushPropertiesTo(layer());

        EXPECT_VECTOR_EQ(gfx::Vector2dF(22, 23), layer()->CurrentScrollOffset());
        EXPECT_VECTOR_EQ(layer()->CurrentScrollOffset(),
            pending_layer->CurrentScrollOffset());
    }

} // namespace
} // namespace cc
